package in.co.codedoc.encode;

import java.util.ArrayList;
import java.util.HashMap;

public class Base64Encoding
{
	public static String Encode(byte[] data,boolean wrap)
	{
		StringBuffer buffer = new StringBuffer();
		
		int i = 0;
		int j = 0;
		byte[] charArray3 = new byte[3];
		
		int begin = 0;
		int end = data.length;

		int outIndex = 0;
		
		while(begin != end)
		{
			charArray3[i++] = data[begin++];

			if (i == 3)
			{
				outIndex = Encode(charArray3, wrap, buffer, outIndex);
				i = 0;
			}
		}

		if (i > 0)
		{
			for(j = i; j < 3; j++)
				charArray3[j] = '\0';

			outIndex = Encode(charArray3, wrap, buffer, outIndex);

			while((i++ < 3))
				buffer.append('=');
		}
		
		return buffer.toString();
	}

	private static int Encode(byte[] charArray3,boolean wrap,StringBuffer buffer,int outIndex)
	{
		int[] charArray4 = new int[4];
		
		charArray4[0] = ((charArray3[0] & 0xfc) >> 2);
		charArray4[1] = (((charArray3[0] & 0x03) << 4) + ((charArray3[1] & 0xf0) >> 4));
		charArray4[2] = (((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6));
		charArray4[3] = (charArray3[2] & 0x3f);

		for(int i = 0; (i <4) ; i++)
		{
			buffer.append(base64Chars[charArray4[i]]);
			++outIndex;
			if(wrap && ((outIndex % 76) == 0))
			{
				buffer.append('\n');
			}
		}
		
		return outIndex;
	}
	
	public static byte[] Decode(String encoded)
	{
		int[] charArray4 = new int[4];

		int begin = 0;
		int end = encoded.length();
		
		ArrayList<Byte> outArray = new ArrayList<Byte>();
		
		int i = 0;
		while(begin != end)
		{
			char c = encoded.charAt(begin++);

			if(c == '=')
			{
				break;
			}

			if(!IsABase64Char(c))
			{
				continue;
			}

			charArray4[i++] = c;

			if (i ==4)
			{
				Decode(charArray4, outArray);
				i = 0;
			}
		}

		if (i > 0)
		{
			for (int j = i; j <4; j++)
				charArray4[j] = 0;

			Decode(charArray4, outArray);
		}

		byte[] retVal = new byte[outArray.size()];
		
		i = 0;
		for(Byte b:outArray)
		{
			retVal[i++] = b;
		}
		return retVal;
	}

	private static void Decode(int[] charArray4,ArrayList<Byte> outArray)
	{
		byte[] charArray3 = new byte[3];
		
		int lastI = 4;
		
		for (int i = 0; i <4; i++)
		{
			if(charArray4[i] == 0)
			{
				lastI = i;
				break;
			}
			charArray4[i] = indexByBase64Char.get((char)charArray4[i]);
		}

		charArray3[0] = (byte)((charArray4[0] << 2) + ((charArray4[1] & 0x30) >> 4));
		charArray3[1] = (byte)(((charArray4[1] & 0xf) << 4) + ((charArray4[2] & 0x3c) >> 2));
		charArray3[2] = (byte)(((charArray4[2] & 0x3) << 6) + charArray4[3]);

		for (int i = 0; (i < (lastI - 1)); i++)
		{
			outArray.add(charArray3[i]);
		}
	}
	
	private static final boolean IsABase64Char(char c)
	{
		return Character.isLetterOrDigit(c) || (c == '+') || (c == '/');
	}
	
	private static char[] base64Chars
		= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
	
	private static HashMap<Character,Integer> indexByBase64Char 
		= new HashMap<Character, Integer>();
	static
	{
		for(int i = 0; i< base64Chars.length;i++)
		{
			indexByBase64Char.put(base64Chars[i], i);
		}
	}

}
